package com.leeyaonan.clinkz.common.util;

import lombok.extern.slf4j.Slf4j;
import net.lingala.zip4j.ZipFile;
import net.lingala.zip4j.exception.ZipException;
import net.lingala.zip4j.model.ZipParameters;
import net.lingala.zip4j.model.enums.AesKeyStrength;
import net.lingala.zip4j.model.enums.EncryptionMethod;
import org.springframework.util.CollectionUtils;

import java.io.File;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 依赖Zip4j，封装与文件压缩相关的方法
 *
 * github：https://github.com/srikanth-lingala/zip4j
 *
 * 帮助文档：https://gitee.com/mirrors/zip4j/blob/master/README.md
 *
 * maven依赖：
 * <dependency>
 * <groupId>net.lingala.zip4j</groupId>
 * <artifactId>zip4j</artifactId>
 * <version>2.9.0</version>
 * </dependency>
 *
 * Zif4j可以实现以下功能，由于使用率和使用场景的差异，该ZipUtils并没有封装所有的功能
 * 【压缩文件】
 * - 压缩单个文件
 * - 压缩多个文件
 * - 压缩整个文件夹
 * - 分卷压缩
 * - 加密
 * 【解压文件】
 * - 解压全部文件
 * - 解压指定文件
 * - 解压文件夹
 * - 解密
 * 【修改压缩文件】
 * - 删除压缩包中的某个文件
 * - 重命名
 * - 合并分卷
 * 【查看、判断】
 * - 查看压缩包文件列表
 * - 判断压缩包是否被密码加密
 * - 判断压缩包是否分卷
 * 【压缩包注释】
 * - 设置注释
 * - 移除注释
 * - 查看注释
 * 【其他功能】
 * - 查看压缩、解压进度
 *
 * @author Rot
 * @date 2021/9/26 10:13
 */
@Slf4j
public class ZipUtils {

    /**
     * 默认分卷长度：10MB
     */
    private static final Long DEFAULT_SPLIT_LENGTH = 10485760L;

    /**
     * 带密码压缩单个文件到指定zip文件（默认使用AES 256加密）
     * Creating a zip file with single file in it / Adding single file to an existing zip
     *
     * @param zipFile   zip文件
     * @param fileToAdd 待压缩文件
     * @param password  密码
     * @throws ZipException
     */
    private static void zipSingleBase(File zipFile, File fileToAdd, String password) {
        try {
            ZipFile zip = createZipFile(zipFile, password);
            ZipParameters zipParameters = createParameters(password);
            zip.addFile(fileToAdd, zipParameters);
        } catch (ZipException e) {
            log.error("[ZipUtils]-压缩文件失败,zipFile={},fileToAdd={},hasPassword={},error={}", zipFile.getName(), fileToAdd.getName(), null != password, e.toString());
        }

        log.info("[ZipUtils]-压缩文件成功,zipFile={},fileToAdd={},hasPassword={}", zipFile.getName(), fileToAdd.getName(), null != password);
    }

    public static void zipSingle(String zipFile, String fileToAdd) {
        zipSingleBase(new File(zipFile), new File(fileToAdd), null);
    }

    public static void zipSingle(String zipFile, String fileToAdd, String password) {
        zipSingleBase(new File(zipFile), new File(fileToAdd), password);
    }

    public static void zipSingle(File zipFile, String fileToAdd) {
        zipSingleBase(zipFile, new File(fileToAdd), null);
    }

    public static void zipSingle(File zipFile, String fileToAdd, String password) {
        zipSingleBase(zipFile, new File(fileToAdd), password);
    }

    public static void zipSingle(File zipFile, File fileToAdd) {
        zipSingleBase(zipFile, fileToAdd, null);
    }

    public static void zipSingle(File zipFile, File fileToAdd, String password) {
        zipSingleBase(zipFile, fileToAdd, password);
    }

    private static ZipFile createZipFile(File zipFile, String password) {
        char[] pwdArr = null;
        if (password != null) {
            pwdArr = password.toCharArray();
        }
        return new ZipFile(zipFile, pwdArr);
    }

    /**
     * 带密码压缩多个个文件到指定zip文件（默认使用AES 256加密）
     * Creating a zip file with multiple files / Adding multiple files to an existing zip
     *
     * @param zipFile
     * @param fileToAddList
     * @param password
     */
    private static void zipMultipleBase(File zipFile, List<File> fileToAddList, String password) {
        try {
            ZipFile zip = createZipFile(zipFile, password);
            ZipParameters zipParameters = createParameters(password);
            zip.addFiles(fileToAddList, zipParameters);
        } catch (ZipException e) {
            log.error("[ZipUtils]-压缩文件失败,zipFile={},fileToAddList={},hasPassword={},error={}", zipFile.getName(), fileToAddList.toString(), null != password, e.toString());
        }

        log.info("[ZipUtils]-压缩文件成功,zipFile={},fileToAddList={},hasPassword={}", zipFile.getName(), fileToAddList.toString(), null != password);

    }

    public static void zipMultiple(String zipFile, List<String> fileToAddList) {
        List<File> files = transfer2FileList(fileToAddList);
        zipMultipleBase(new File(zipFile), files, null);
    }

    public static void zipMultiple(String zipFile, List<String> fileToAddList, String password) {
        List<File> files = transfer2FileList(fileToAddList);
        zipMultipleBase(new File(zipFile), files, password);
    }

    public static void zipMultiple(File zipFile, List<File> fileToAddList) {
        zipMultipleBase(zipFile, fileToAddList, null);
    }

    public static void zipMultiple(File zipFile, List<File> fileToAddList, String password) {
        zipMultipleBase(zipFile, fileToAddList, password);
    }


    private static List<File> transfer2FileList(List<String> fileToAddList) {
        return fileToAddList.stream().map(File::new).collect(Collectors.toList());
    }

    /**
     * 压缩整个文件夹到指定文件
     * Creating a zip file by adding a folder to it / Adding a folder to an existing zip
     *
     * @param zipFile
     * @param folderToAdd
     * @param password
     */
    private static void zipFolderBase(File zipFile, File folderToAdd, String password) {
        try {
            ZipFile zip = createZipFile(zipFile, password);
            ZipParameters zipParameters = createParameters(password);
            zip.addFolder(folderToAdd, zipParameters);
        } catch (ZipException e) {
            log.error("[ZipUtils]-压缩文件夹失败,zipFile={},folderToAdd={},hasPassword={},error={}", zipFile.getName(), folderToAdd.getName(), null != password, e.toString());
        }

        log.info("[ZipUtils]-压缩文件夹成功,zipFile={},folderToAdd={},hasPassword={}", zipFile.getName(), folderToAdd.getName(), null != password);
    }

    public static void zipFolder(String zipFile, String folderToAdd) {
        zipFolderBase(new File(zipFile), new File(folderToAdd), null);
    }

    public static void zipFolder(String zipFile, String folderToAdd, String password) {
        zipFolderBase(new File(zipFile), new File(folderToAdd), password);
    }

    public static void zipFolder(File zipFile, String folderToAdd) {
        zipFolderBase(zipFile, new File(folderToAdd), null);
    }

    public static void zipFolder(File zipFile, String folderToAdd, String password) {
        zipFolderBase(zipFile, new File(folderToAdd), password);
    }

    public static void zipFolder(File zipFile, File folderToAdd) {
        zipFolderBase(zipFile, folderToAdd, null);
    }

    public static void zipFolder(File zipFile, File folderToAdd, String password) {
        zipFolderBase(zipFile, folderToAdd, password);
    }

    private static ZipParameters createParameters(String password) {
        ZipParameters zipParameters = new ZipParameters();
        if (null != password) {
            zipParameters.setEncryptFiles(true);
            zipParameters.setEncryptionMethod(EncryptionMethod.AES);
            // Below line is optional. AES 256 is used by default. You can override it to use AES 128. AES 192 is supported only for extracting.
            zipParameters.setAesKeyStrength(AesKeyStrength.KEY_STRENGTH_256);
        }
        return zipParameters;
    }

    /**
     * 分卷压缩文件（指定分卷大小，单位：kb）
     *
     * @param zipFile
     * @param fileToAddList
     * @param password
     * @param kb
     */
    private static void zipSplitFileBase(File zipFile, List<File> fileToAddList, String password, Long kb) {
        try {
            ZipFile zip = createZipFile(zipFile, password);
            ZipParameters zipParameters = createParameters(password);
            zip.createSplitZipFile(fileToAddList, zipParameters, true, kb);
        } catch (ZipException e) {
            log.error("[ZipUtils]-分卷压缩文件失败,zipFile={},fileToAddList={},hasPassword={},error={}", zipFile.getName(), fileToAddList.toString(), null != password, e.toString());
        }

        log.info("[ZipUtils]-分卷压缩文件成功,zipFile={},fileToAddList={},hasPassword={}", zipFile.getName(), fileToAddList.toString(), null != password);

    }

    public static void zipSplitFile(String zipFile, List<String> fileToAddList) {
        zipSplitFileBase(new File(zipFile), transfer2FileList(fileToAddList), null, DEFAULT_SPLIT_LENGTH);
    }

    public static void zipSplitFile(String zipFile, List<String> fileToAddList, Long kb) {
        zipSplitFileBase(new File(zipFile), transfer2FileList(fileToAddList), null, kb);
    }

    public static void zipSplitFile(String zipFile, List<String> fileToAddList, String password) {
        zipSplitFileBase(new File(zipFile), transfer2FileList(fileToAddList), password, DEFAULT_SPLIT_LENGTH);
    }

    public static void zipSplitFile(String zipFile, List<String> fileToAddList, String password, Long kb) {
        zipSplitFileBase(new File(zipFile), transfer2FileList(fileToAddList), password, kb);
    }

    public static void zipSplitFile(File zipFile, List<File> fileToAddList) {
        zipSplitFileBase(zipFile, fileToAddList, null, DEFAULT_SPLIT_LENGTH);
    }

    public static void zipSplitFile(File zipFile, List<File> fileToAddList, Long kb) {
        zipSplitFileBase(zipFile, fileToAddList, null, kb);
    }

    public static void zipSplitFile(File zipFile, List<File> fileToAddList, String password) {
        zipSplitFileBase(zipFile, fileToAddList, password, DEFAULT_SPLIT_LENGTH);
    }

    public static void zipSplitFile(File zipFile, List<File> fileToAddList, String password, Long kb) {
        zipSplitFileBase(zipFile, fileToAddList, password, kb);
    }

    public static void zipSplitFile(String zipFile, String fileToAdd) {
        zipSplitFileBase(new File(zipFile), Collections.singletonList(new File(fileToAdd)), null, DEFAULT_SPLIT_LENGTH);
    }

    public static void zipSplitFile(String zipFile, String fileToAdd, Long kb) {
        zipSplitFileBase(new File(zipFile), Collections.singletonList(new File(fileToAdd)), null, kb);
    }

    public static void zipSplitFile(String zipFile, String fileToAdd, String password) {
        zipSplitFileBase(new File(zipFile), Collections.singletonList(new File(fileToAdd)), password, DEFAULT_SPLIT_LENGTH);
    }

    public static void zipSplitFile(String zipFile, String fileToAdd, String password, Long kb) {
        zipSplitFileBase(new File(zipFile), Collections.singletonList(new File(fileToAdd)), password, kb);
    }

    public static void zipSplitFile(File zipFile, File fileToAdd) {
        zipSplitFileBase(zipFile, Collections.singletonList(fileToAdd), null, DEFAULT_SPLIT_LENGTH);
    }

    public static void zipSplitFile(File zipFile, File fileToAdd, Long kb) {
        zipSplitFileBase(zipFile, Collections.singletonList(fileToAdd), null, kb);
    }

    public static void zipSplitFile(File zipFile, File fileToAdd, String password) {
        zipSplitFileBase(zipFile, Collections.singletonList(fileToAdd), password, DEFAULT_SPLIT_LENGTH);
    }

    public static void zipSplitFile(File zipFile, File fileToAdd, String password, Long kb) {
        zipSplitFileBase(zipFile, Collections.singletonList(fileToAdd), password, kb);
    }
}
